package org.feng.navigation.common.util;

import cn.hutool.core.collection.CollectionUtil;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * 分组工具类：提供分组方法，按照对象1个或多个属性分组。
 *
 * @version V1.0
 * @author: junzi
 * @date: 2023年01月12日 10时50分
 */
public class GroupByUtil {
    /**
     * 将数据按照某几个属性分组
     *
     * @param list      原始数据列表
     * @param functions 数据对象获取指定属性，可以是0或多个
     * @param <T>       原始数据类型
     * @return 分组后的数据， 键是多个属性，比如按照店铺、平台分，键就是 {@code {shopId,platformId}} 的一个列表
     */
    @SafeVarargs
    public static <T> Map<List<Object>, List<T>> groupingBy(List<T> list, Function<T, Object>... functions) {
        if (CollectionUtil.isEmpty(list)) {
            return Collections.emptyMap();
        }
        if (functions.length == 0) {
            return Collections.singletonMap(List.of(), list);
        }
        return list.stream().collect(Collectors.groupingBy(order -> {
            List<Object> keys = new ArrayList<>();
            for (Function<T, Object> tObjectFunction : functions) {
                keys.add(tObjectFunction.apply(order));
            }
            return keys;
        }, Collectors.toList()));
    }

    /**
     * 通过一个整数分组
     *
     * @param list     原始数据列表
     * @param function 数据对象获取指定属性，需要是一个整数
     * @param <T>      原始数据类型
     * @return 分组后的数据， 键是一个整数
     */
    public static <T> Map<Integer, List<T>> groupingByInteger(List<T> list, Function<T, Integer> function) {
        return groupingBySingleParam(list, function);
    }

    /**
     * 通过一个字符串分组
     *
     * @param list     原始数据列表
     * @param function 数据对象获取指定属性，需要是一个字符串
     * @param <T>      原始数据类型
     * @return 分组后的数据， 键是一个字符串
     */
    public static <T> Map<String, List<T>> groupingByString(List<T> list, Function<T, String> function) {
        return groupingBySingleParam(list, function);
    }

    /**
     * 通过一个 指定类型K 分组
     *
     * @param list     原始数据列表
     * @param function 数据对象获取指定属性，需要是 K类型
     * @param <T>      原始数据类型
     * @param <K>      分组键的类型
     * @return 分组后的数据， 键是K类型
     */
    public static <K, T> Map<K, List<T>> groupingBySingleParam(List<T> list, Function<T, K> function) {
        if (CollectionUtil.isEmpty(list)) {
            return Collections.emptyMap();
        }
        return list.stream().collect(Collectors.groupingBy(function, Collectors.toList()));
    }

    private GroupByUtil() {
    }
}
